package cn.heycm.tenant.common.aspect;

import cn.heycm.tenant.common.annotation.Tenant;
import cn.heycm.tenant.common.constant.TenantConstant;
import cn.heycm.tenant.common.context.TenantContextHolder;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

/**
 * 租户切面，设置租户上下文，设置切面最高优先级（否则与事务注解在同一个方法使用时会失效）
 *
 * @author hey
 * @version 1.0
 * @since 2024/8/24 22:50
 */
@Component
@Aspect
@Order(Ordered.HIGHEST_PRECEDENCE)
@Slf4j
public class TenantAspect {

    /**
     * 环绕通知
     * 1.执行前设置租户上下文
     * 2.执行后清除租户上下文
     */
    @Around("@annotation(cn.heycm.tenant.common.annotation.Tenant)")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        String tenant = this.getTenant(pjp);
        if (!StringUtils.hasText(tenant)) {
            throw new RuntimeException("Tenant context is missing.");
        }
        if (TenantContextHolder.exist()) {
            log.debug("Tenant context switches from [{}] to [{}]", TenantContextHolder.get(), tenant);
        } else {
            log.debug("Tenant context switches to [{}]", tenant);
        }
        TenantContextHolder.set(tenant);
        try {
            return pjp.proceed();
        } finally {
            if (TenantContextHolder.exist()) {
                log.debug("Tenant context [{}] removed.", TenantContextHolder.get());
                TenantContextHolder.clear();
            } else {
                log.warn("Tenant context has already been removed, Please check the nested use of @Tenant.");
            }
        }
    }

    /**
     * 获取租户
     * 1.优先取注解设定的租户
     * 2.取不到时，从请求头取
     *
     * @return 租户ID
     */
    private String getTenant(ProceedingJoinPoint pjp) {
        // 优先取注解设定的租户
        MethodSignature signature = (MethodSignature) pjp.getSignature();
        Tenant tenant = signature.getMethod().getAnnotation(Tenant.class);
        if (StringUtils.hasText(tenant.value())) {
            return tenant.value();
        }
        // 取不到时，从请求头取
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        if (null == attributes) {
            return null;
        }
        HttpServletRequest request = attributes.getRequest();
        return request.getHeader(TenantConstant.HEADER);
    }
}
